using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Threading;

namespace WindowsService1
{
    /// <summary>
    /// 主题分类日志记录器（支持MQTT主题和通用分类日志）
    /// 按主题/分类和日期自动创建日志文件，格式：Log\LogMqtt\年月日\主题名.log 或 Log\LogCategory\年月日\分类名.log
    /// </summary>
    public class MqttTopicLogger
    {
        private static readonly Lazy<MqttTopicLogger> _instance = new Lazy<MqttTopicLogger>(() => new MqttTopicLogger());
        public static MqttTopicLogger Instance => _instance.Value;

        private readonly ConcurrentDictionary<string, object> _topicLocks = new ConcurrentDictionary<string, object>();
        private readonly string _mqttLogPath;
        private readonly string _categoryLogPath;

        private MqttTopicLogger()
        {
            var baseDir = AppDomain.CurrentDomain.BaseDirectory;
            _mqttLogPath = Path.Combine(baseDir, "Log", "LogMqtt");
            _categoryLogPath = Path.Combine(baseDir, "Log", "LogCategory");
            EnsureDirectoryExists(_mqttLogPath);
            EnsureDirectoryExists(_categoryLogPath);
        }

        /// <summary>
        /// 写入分类日志的通用方法
        /// </summary>
        /// <param name="category">分类名称</param>
        /// <param name="message">日志消息</param>
        /// <param name="logLevel">日志级别</param>
        /// <param name="logType">日志类型：MQTT 或 CATEGORY</param>
        private void WriteLog(string category, string message, string logLevel, string logType)
        {
            if (string.IsNullOrEmpty(category) || string.IsNullOrEmpty(message))
                return;

            try
            {
                // 清理分类名作为文件名（移除非法字符）
                string sanitizedCategory = SanitizeFileName(category);
                
                // 根据日志类型选择基础路径
                string basePath = logType == "MQTT" ? _mqttLogPath : _categoryLogPath;
                
                // 获取当前日期目录
                string dateFolder = DateTime.Now.ToString("yyyyMMdd");
                string logDirectory = Path.Combine(basePath, dateFolder);
                EnsureDirectoryExists(logDirectory);

                // 构建日志文件路径
                string logFileName = $"{sanitizedCategory}.log";
                string logFilePath = Path.Combine(logDirectory, logFileName);

                // 获取或创建该分类的锁对象
                string lockKey = $"{logType}_{sanitizedCategory}";
                object lockObj = _topicLocks.GetOrAdd(lockKey, _ => new object());

                // 构建日志内容
                string timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
                string categoryLabel = logType == "MQTT" ? "主题" : "分类";
                string logEntry = $"[{timestamp}] [{logLevel}] [{categoryLabel}:{category}] {message}{Environment.NewLine}";

                // 线程安全地写入文件
                lock (lockObj)
                {
                    File.AppendAllText(logFilePath, logEntry);
                }
            }
            catch (Exception ex)
            {
                // 写入主日志文件作为备用
                Logger.WriteError($"{logType}日志写入失败: {ex.Message}");
            }
        }

        /// <summary>
        /// 写入MQTT主题日志
        /// </summary>
        /// <param name="topic">MQTT主题</param>
        /// <param name="message">日志消息</param>
        /// <param name="logLevel">日志级别</param>
        public void WriteTopicLog(string topic, string message, string logLevel = "INFO")
        {
            WriteLog(topic, message, logLevel, "MQTT");
        }

        /// <summary>
        /// 写入通用分类日志
        /// </summary>
        /// <param name="category">分类名称</param>
        /// <param name="message">日志消息</param>
        /// <param name="logLevel">日志级别</param>
        public void WriteCategoryLog(string category, string message, string logLevel = "INFO")
        {
            WriteLog(category, message, logLevel, "CATEGORY");
        }

        /// <summary>
        /// 写入MQTT消息接收日志
        /// </summary>
        /// <param name="topic">MQTT主题</param>
        /// <param name="message">MQTT消息内容</param>
        /// <param name="source">消息来源</param>
        public void WriteMessageReceived(string topic, string message, string source = "")
        {
            string logMessage = string.IsNullOrEmpty(source) 
                ? $"接收消息: {message}"
                : $"接收消息 [来源:{source}]: {message}";
            
            WriteTopicLog(topic, logMessage, "MQTT_IN");
        }

        /// <summary>
        /// 写入MQTT消息发送日志
        /// </summary>
        /// <param name="topic">MQTT主题</param>
        /// <param name="message">MQTT消息内容</param>
        /// <param name="source">消息发送者</param>
        public void WriteMessageSent(string topic, string message, string source = "")
        {
            string logMessage = string.IsNullOrEmpty(source) 
                ? $"发送消息: {message}"
                : $"发送消息 [发送者:{source}]: {message}";
            
            WriteTopicLog(topic, logMessage, "MQTT_OUT");
        }

        /// <summary>
        /// 写入MQTT处理日志
        /// </summary>
        /// <param name="topic">MQTT主题</param>
        /// <param name="message">处理日志消息</param>
        /// <param name="processType">处理类型</param>
        public void WriteProcessLog(string topic, string message, string processType = "")
        {
            string logMessage = string.IsNullOrEmpty(processType) 
                ? $"处理: {message}"
                : $"处理 [{processType}]: {message}";
            
            WriteTopicLog(topic, logMessage, "PROCESS");
        }

        /// <summary>
        /// 写入MQTT错误日志
        /// </summary>
        /// <param name="topic">MQTT主题</param>
        /// <param name="message">错误消息</param>
        /// <param name="exception">异常对象</param>
        public void WriteErrorLog(string topic, string message, Exception exception = null)
        {
            string logMessage = exception == null 
                ? $"错误: {message}"
                : $"错误: {message} | 异常: {exception.Message}";
            
            WriteTopicLog(topic, logMessage, "ERROR");
        }

        /// <summary>
        /// 写入参数操作日志
        /// </summary>
        /// <param name="topic">MQTT主题</param>
        /// <param name="operation">操作类型（如：写入参数、读取参数等）</param>
        /// <param name="parameterCode">参数代码</param>
        /// <param name="parameterValue">参数值</param>
        /// <param name="result">操作结果</param>
        public void WriteParameterLog(string topic, string operation, string parameterCode, string parameterValue, string result)
        {
            string logMessage = $"{operation}: {parameterCode} = {parameterValue} | 结果: {result}";
            WriteTopicLog(topic, logMessage, "PARAMETER");
        }

        #region 通用分类日志便捷方法

        /// <summary>
        /// 写入系统信息日志
        /// </summary>
        /// <param name="message">信息消息</param>
        public void WriteSystemInfo(string message)
        {
            WriteCategoryLog("SYSTEM_INFO", message, "INFO");
        }

        /// <summary>
        /// 写入系统错误日志
        /// </summary>
        /// <param name="message">错误消息</param>
        /// <param name="exception">异常对象</param>
        public void WriteSystemError(string message, Exception exception = null)
        {
            string logMessage = exception == null 
                ? message
                : $"{message} | 异常: {exception.Message} | 堆栈: {exception.StackTrace}";
            WriteCategoryLog("SYSTEM_ERROR", logMessage, "ERROR");
        }

        /// <summary>
        /// 写入PLC操作日志
        /// </summary>
        /// <param name="operation">操作类型</param>
        /// <param name="address">PLC地址</param>
        /// <param name="value">数据值</param>
        /// <param name="result">操作结果</param>
        public void WritePLCOperation(string operation, string address, string value, string result)
        {
            string logMessage = $"{operation}: 地址={address}, 值={value}, 结果={result}";
            WriteCategoryLog("PLC_OPERATION", logMessage, "PLC");
        }

        /// <summary>
        /// 写入数据库操作日志
        /// </summary>
        /// <param name="operation">操作类型</param>
        /// <param name="sql">SQL语句</param>
        /// <param name="result">操作结果</param>
        public void WriteDatabaseOperation(string operation, string sql, string result)
        {
            string logMessage = $"{operation}: SQL={sql}, 结果={result}";
            WriteCategoryLog("DATABASE", logMessage, "DB");
        }

        /// <summary>
        /// 写入配置操作日志
        /// </summary>
        /// <param name="operation">操作类型</param>
        /// <param name="configItem">配置项</param>
        /// <param name="value">配置值</param>
        public void WriteConfigOperation(string operation, string configItem, string value)
        {
            string logMessage = $"{operation}: {configItem} = {value}";
            WriteCategoryLog("CONFIG", logMessage, "CONFIG");
        }

        /// <summary>
        /// 写入服务状态日志
        /// </summary>
        /// <param name="service">服务名称</param>
        /// <param name="status">状态</param>
        /// <param name="details">详细信息</param>
        public void WriteServiceStatus(string service, string status, string details = "")
        {
            string logMessage = string.IsNullOrEmpty(details)
                ? $"{service}: {status}"
                : $"{service}: {status} | {details}";
            WriteCategoryLog("SERVICE_STATUS", logMessage, "SERVICE");
        }

        /// <summary>
        /// 写入性能监控日志
        /// </summary>
        /// <param name="metric">性能指标</param>
        /// <param name="value">指标值</param>
        /// <param name="unit">单位</param>
        public void WritePerformanceMetric(string metric, double value, string unit = "")
        {
            string logMessage = string.IsNullOrEmpty(unit)
                ? $"{metric}: {value}"
                : $"{metric}: {value} {unit}";
            WriteCategoryLog("PERFORMANCE", logMessage, "PERF");
        }

        /// <summary>
        /// 写入通用警告日志
        /// </summary>
        /// <param name="category">警告分类</param>
        /// <param name="message">警告消息</param>
        public void WriteWarning(string category, string message)
        {
            WriteCategoryLog($"WARNING_{category.ToUpper()}", message, "WARN");
        }

        #endregion

        /// <summary>
        /// 清理文件名中的非法字符
        /// </summary>
        /// <param name="fileName">原始文件名</param>
        /// <returns>清理后的文件名</returns>
        private string SanitizeFileName(string fileName)
        {
            if (string.IsNullOrEmpty(fileName))
                return "unknown";

            // 替换MQTT主题中的特殊字符
            string sanitized = fileName
                .Replace("/", "_")
                .Replace("\\", "_")
                .Replace(":", "_")
                .Replace("*", "_")
                .Replace("?", "_")
                .Replace("\"", "_")
                .Replace("<", "_")
                .Replace(">", "_")
                .Replace("|", "_")
                .Replace("+", "PLUS")
                .Replace("#", "HASH");

            // 限制文件名长度
            if (sanitized.Length > 100)
            {
                sanitized = sanitized.Substring(0, 100);
            }

            return sanitized;
        }

        /// <summary>
        /// 确保目录存在
        /// </summary>
        /// <param name="directoryPath">目录路径</param>
        private void EnsureDirectoryExists(string directoryPath)
        {
            try
            {
                if (!Directory.Exists(directoryPath))
                {
                    Directory.CreateDirectory(directoryPath);
                }
            }
            catch (Exception ex)
            {
                Logger.WriteError($"创建目录失败: {directoryPath}, 错误: {ex.Message}");
            }
        }

        /// <summary>
        /// 清理过期的日志文件（保留指定天数的日志）
        /// </summary>
        /// <param name="retentionDays">保留天数，默认30天</param>
        /// <param name="logType">日志类型：ALL, MQTT, CATEGORY</param>
        public void CleanupOldLogs(int retentionDays = 30, string logType = "ALL")
        {
            try
            {
                DateTime cutoffDate = DateTime.Now.AddDays(-retentionDays);

                // 清理MQTT日志
                if (logType == "ALL" || logType == "MQTT")
                {
                    CleanupLogDirectory(_mqttLogPath, cutoffDate, "MQTT");
                }

                // 清理分类日志
                if (logType == "ALL" || logType == "CATEGORY")
                {
                    CleanupLogDirectory(_categoryLogPath, cutoffDate, "分类");
                }
            }
            catch (Exception ex)
            {
                Logger.WriteError($"清理过期日志时出错: {ex.Message}");
            }
        }

        /// <summary>
        /// 清理指定目录的过期日志
        /// </summary>
        private void CleanupLogDirectory(string basePath, DateTime cutoffDate, string logTypeName)
        {
            if (!Directory.Exists(basePath))
                return;

            string[] dateFolders = Directory.GetDirectories(basePath);

            foreach (string dateFolder in dateFolders)
            {
                string folderName = Path.GetFileName(dateFolder);
                
                // 尝试解析文件夹名为日期（yyyyMMdd格式）
                if (DateTime.TryParseExact(folderName, "yyyyMMdd", null, 
                    System.Globalization.DateTimeStyles.None, out DateTime folderDate))
                {
                    if (folderDate < cutoffDate)
                    {
                        Directory.Delete(dateFolder, true);
                        Logger.WriteInfo($"已删除过期的{logTypeName}日志目录: {dateFolder}");
                    }
                }
            }
        }

        /// <summary>
        /// 获取指定主题的日志文件路径
        /// </summary>
        /// <param name="topic">MQTT主题</param>
        /// <param name="date">日期，默认为今天</param>
        /// <returns>日志文件路径</returns>
        public string GetTopicLogFilePath(string topic, DateTime? date = null)
        {
            return GetLogFilePath(topic, date, "MQTT");
        }

        /// <summary>
        /// 获取指定分类的日志文件路径
        /// </summary>
        /// <param name="category">分类名称</param>
        /// <param name="date">日期，默认为今天</param>
        /// <returns>日志文件路径</returns>
        public string GetCategoryLogFilePath(string category, DateTime? date = null)
        {
            return GetLogFilePath(category, date, "CATEGORY");
        }

        /// <summary>
        /// 获取指定分类的日志文件路径（通用方法）
        /// </summary>
        private string GetLogFilePath(string category, DateTime? date, string logType)
        {
            DateTime logDate = date ?? DateTime.Now;
            string dateFolder = logDate.ToString("yyyyMMdd");
            string sanitizedCategory = SanitizeFileName(category);
            string logFileName = $"{sanitizedCategory}.log";
            
            string basePath = logType == "MQTT" ? _mqttLogPath : _categoryLogPath;
            return Path.Combine(basePath, dateFolder, logFileName);
        }

        /// <summary>
        /// 获取所有主题的日志统计信息
        /// </summary>
        /// <param name="date">日期，默认为今天</param>
        /// <returns>主题日志统计</returns>
        public Dictionary<string, long> GetTopicLogStatistics(DateTime? date = null)
        {
            return GetLogStatistics(date, "MQTT");
        }

        /// <summary>
        /// 获取所有分类的日志统计信息
        /// </summary>
        /// <param name="date">日期，默认为今天</param>
        /// <returns>分类日志统计</returns>
        public Dictionary<string, long> GetCategoryLogStatistics(DateTime? date = null)
        {
            return GetLogStatistics(date, "CATEGORY");
        }

        /// <summary>
        /// 获取所有日志统计信息（通用方法）
        /// </summary>
        private Dictionary<string, long> GetLogStatistics(DateTime? date, string logType)
        {
            var statistics = new Dictionary<string, long>();
            
            try
            {
                DateTime logDate = date ?? DateTime.Now;
                string dateFolder = logDate.ToString("yyyyMMdd");
                string basePath = logType == "MQTT" ? _mqttLogPath : _categoryLogPath;
                string logDirectory = Path.Combine(basePath, dateFolder);

                if (!Directory.Exists(logDirectory))
                    return statistics;

                string[] logFiles = Directory.GetFiles(logDirectory, "*.log");
                
                foreach (string logFile in logFiles)
                {
                    string fileName = Path.GetFileNameWithoutExtension(logFile);
                    FileInfo fileInfo = new FileInfo(logFile);
                    statistics[fileName] = fileInfo.Length;
                }
            }
            catch (Exception ex)
            {
                Logger.WriteError($"获取{logType}日志统计信息时出错: {ex.Message}");
            }

            return statistics;
        }
    }
}
